
module aru_binary_div (
    input logic              clk,
    input logic              rst_n,
          aru_div_cfg_if.in  u_aru_cfg_if,
          aru_payload_if.in  u_aru_pld_top_if,
          aru_payload_if.in  u_aru_pld_left_if,
          aru_payload_if.in  u_aru_pld_bottom_if,
          aru_payload_if.out u_aru_pld_right_if
);
    logic lst_req_in_instr;
    assign lst_req_in_instr = u_aru_pld_right_if.sdb.eom && u_aru_pld_right_if.sdb.eon;

    // cfg handshake
    logic cfg_rdy, cfg_vld;
    assign cfg_vld = ~cfg_rdy;
    always_ff @(posedge clk or negedge rst_n) begin
        if (!rst_n) begin
            cfg_rdy <= 1'b1;
        end else if (cfg_rdy == 1'b0) begin
            if (u_aru_pld_right_if.vld && u_aru_pld_right_if.rdy && lst_req_in_instr) begin
                cfg_rdy <= 1'b1;
            end
        end else begin
            if (u_aru_cfg_if.vld && u_aru_cfg_if.rdy) begin
                cfg_rdy <= 1'b0;
            end
        end
    end

    // bf16_t scalar_vector[`P_ARU*`N0-1:0];
    aru_dat_t scalar_vector;
    always_comb begin
        for (int i = 0; i < `P_ARU * `N0; i = i + 1) begin
            scalar_vector.dat[i] = u_aru_cfg_if.scalar;
        end
    end

    logic [1:0] sel_3_to_1;
    always_comb begin
        case (u_aru_cfg_if.mode)
            3'd0: sel_3_to_1 = 2'd2;
            3'd2: sel_3_to_1 = 2'd0;
            3'd3: sel_3_to_1 = 2'd2;
            3'd4: sel_3_to_1 = 2'd1;
            default: sel_3_to_1 = 2'd0;
        endcase
    end

    // bf16_t dat_3_to_1_top[`P_ARU*`N0-1:0];
    aru_dat_t dat_3_to_1_top;
    common_mux_n_to_1 #(
        .WIDTH(`P_ARU * `N0 * $bits(bf16_t)),
        .N    (3)
    ) u_mux_3_to_1_top (
        .data_in ({u_aru_pld_left_if.dat, scalar_vector, u_aru_pld_top_if.dat}),
        .sel     (sel_3_to_1),
        .data_out(dat_3_to_1_top.dat)
    );



    aru_dat_t div_out;
    logic     data_vld;
    logic     div_out_vld[`P_ARU*`N0-1:0];
    genvar i;
    for (i = 0; i < `P_ARU * `N0; i = i + 1) begin : gen_div
        bf16_fpdiv u_bf16_fpdiv (
            .clk         (clk),
            .rst         (!rst_n),
            .clock_enable(u_aru_pld_right_if.rdy && cfg_vld),
            .in_valid    (data_vld),
            .X           (dat_3_to_1_top.dat[i]),
            .Y           (u_aru_pld_bottom_if.dat.dat[i]),
            .R           (div_out.dat[i]),
            .out_valid   (div_out_vld[i])
        );
    end

    logic sel_2_to_1_left;
    always_comb begin
        case (u_aru_cfg_if.mode)
            3'd0: sel_2_to_1_left = 1'd0;
            3'd1: sel_2_to_1_left = 1'd1;
            3'd2: sel_2_to_1_left = 1'd0;
            default: sel_2_to_1_left = 1'd0;
        endcase
    end

    aru_dat_t dat_2_to_1_left;
    common_mux_n_to_1 #(
        .WIDTH(`P_ARU * `N0 * $bits(bf16_t)),
        .N    (2)
    ) u_mux_2_to_1_left (
        .data_in ({u_aru_pld_bottom_if.dat, dat_3_to_1_top.dat}),
        .sel     (sel_2_to_1_left),
        .data_out(dat_2_to_1_left.dat)
    );

    aru_dat_t dat_2_to_1_left_delayed;
    common_delay_line #(
        .WIDTH(`P_ARU * `N0 * $bits(bf16_t)),
        .DEPTH(`DIV_LATENCY - 1)
    ) u_data_delay_line (
        .clk  (clk),
        .rst_n(rst_n),
        .en   (u_aru_pld_right_if.rdy && cfg_vld),
        .data_in  (dat_2_to_1_left.dat),
        .data_out (dat_2_to_1_left_delayed.dat)
    );

    logic sel_2_to_1_right;
    always_comb begin
        case (u_aru_cfg_if.mode)
            3'd0: sel_2_to_1_right = 1'd0;
            3'd1: sel_2_to_1_right = 1'd0;
            3'd2: sel_2_to_1_right = 1'd0;
            3'd3: sel_2_to_1_right = 1'd1;
            3'd4: sel_2_to_1_right = 1'd1;
            default: sel_2_to_1_right = 1'd0;
        endcase
    end

    aru_dat_t dat_2_to_1_right;
    common_mux_n_to_1 #(
        .WIDTH(`P_ARU * `N0 * $bits(bf16_t)),
        .N    (2)
    ) u_mux_2_to_1_right (
        .data_in ({div_out.dat, dat_2_to_1_left_delayed.dat}),
        .sel     (sel_2_to_1_right),
        .data_out(dat_2_to_1_right.dat)
    );


    logic delayed_data_vld;
    common_delay_line #(
        .WIDTH(1),
        .DEPTH(`DIV_LATENCY - 1)
    ) u_valid_delay_line (
        .clk  (clk),
        .rst_n(rst_n),
        .en   (u_aru_pld_right_if.rdy && cfg_vld),
        .data_in  (data_vld),
        .data_out (delayed_data_vld)
    );

    aru_sdb_t selected_sdb;
    aru_sdb_t delayed_sdb;
    common_delay_line #(
        .WIDTH($bits(aru_sdb_t)),
        .DEPTH(`DIV_LATENCY - 1)
    ) u_sdb_delay_line (
        .clk  (clk),
        .rst_n(rst_n),
        .en   (u_aru_pld_right_if.rdy && cfg_vld),
        .data_in  (selected_sdb),
        .data_out (delayed_sdb)
    );

    always_comb begin
        case (u_aru_cfg_if.mode)
            3'd0: begin
                u_aru_pld_top_if.rdy    = u_aru_pld_right_if.rdy && cfg_vld;
                u_aru_pld_left_if.rdy   = 1'b0;
                u_aru_pld_bottom_if.rdy = 1'b0;
            end
            3'd1: begin
                u_aru_pld_top_if.rdy    = 1'b0;
                u_aru_pld_left_if.rdy   = 1'b0;
                u_aru_pld_bottom_if.rdy = u_aru_pld_right_if.rdy && cfg_vld;
            end
            3'd2: begin
                u_aru_pld_top_if.rdy    = 1'b0;
                u_aru_pld_left_if.rdy   = u_aru_pld_right_if.rdy && cfg_vld;
                u_aru_pld_bottom_if.rdy = 1'b0;
            end
            3'd3: begin
                u_aru_pld_top_if.rdy    = u_aru_pld_bottom_if.vld && u_aru_pld_right_if.rdy && cfg_vld;
                u_aru_pld_left_if.rdy   = 1'b0;
                u_aru_pld_bottom_if.rdy = u_aru_pld_top_if.vld && u_aru_pld_right_if.rdy && cfg_vld;
            end
            3'd4: begin
                u_aru_pld_top_if.rdy    = 1'b0;
                u_aru_pld_left_if.rdy   = 1'b0;
                u_aru_pld_bottom_if.rdy = u_aru_pld_right_if.rdy && cfg_vld;
            end
            default: begin
                u_aru_pld_top_if.rdy    = 1'b0;
                u_aru_pld_left_if.rdy   = 1'b0;
                u_aru_pld_bottom_if.rdy = 1'b0;
            end
        endcase
    end

    always_comb begin
        case (u_aru_cfg_if.mode)
            3'd0: begin
                data_vld     = u_aru_pld_top_if.vld;
                selected_sdb = u_aru_pld_top_if.sdb;
            end
            3'd1: begin
                data_vld     = u_aru_pld_bottom_if.vld;
                selected_sdb = u_aru_pld_bottom_if.sdb;
            end
            3'd2: begin
                data_vld     = u_aru_pld_left_if.vld;
                selected_sdb = u_aru_pld_left_if.sdb;
            end
            3'd3: begin
                data_vld     = u_aru_pld_top_if.vld && u_aru_pld_bottom_if.vld;
                selected_sdb = u_aru_pld_top_if.sdb;
            end
            3'd4: begin
                data_vld     = u_aru_pld_bottom_if.vld;
                selected_sdb = u_aru_pld_bottom_if.sdb;
            end
            default: begin
                data_vld     = 1'b0;
                selected_sdb = '0;
            end
        endcase
    end

    assign u_aru_pld_right_if.dat = dat_2_to_1_right.dat;
    assign u_aru_pld_right_if.vld = delayed_data_vld;
    assign u_aru_pld_right_if.sdb = delayed_sdb;
endmodule
